/*************************************************************************** 
 * (c) 2006 Dmitry Osinovsky, osdmsoftware@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License as        *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 ***************************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using OsdmSoftware.CommonLibrary;

namespace OsdmSoftware.PocketPicture
{
    public enum PaletteColorNumber { [Label("16")] Colors16 = 0, [Label("64")] Colors64 = 1, [Label("125")] Colors125 = 2 }
    public partial class PaletteControl: Control, IDropDownControl
    {
        List<Color> colorList = new List<Color>();
        int colorsInRow;
        int rectangleSize;
        Color selectedColor;
        int spanSize;

        public List<Color> ColorList { get { return colorList; } }
        public int ColorsInRow { get { return colorsInRow; } set { colorsInRow = value; } }
        public int RectangleSize { get { return rectangleSize; } set { rectangleSize = value; } }
        public int SpanSize { get { return spanSize; } set { spanSize = value; } }
        public Color SelectedColor
        {
            get
            {
                return selectedColor;
            }
            set
            {
                if (!selectedColor.Equals(value))
                {
                    selectedColor = value;
                    Refresh();
                    if (ColorSelected != null)
                    {
                        ColorSelected(this, EventArgs.Empty);
                    }
                }
            }
        }


        public event EventHandler ColorSelected;
        
        public PaletteControl()
        {
            InitializeComponent();
        }

        public void SetPalette(PaletteColorNumber colorNumber)
        {
            colorList.Clear();
            if (colorNumber == PaletteColorNumber.Colors64)
            {
                for (int red = 0; red <= 256; red += 85)
                    for (int green = 0; green <= 256; green += 85)
                        for (int blue = 0; blue <= 256; blue += 85)
                        {
                            colorList.Add(Color.FromArgb(Math.Min(red, 255), Math.Min(green, 255), Math.Min(blue, 255)));
                        }
            }
            else if (colorNumber == PaletteColorNumber.Colors16)
            {
                for (int bright = 127; bright <= 255; bright += 128)
                    for (int red = 0; red <= 1; red++)
                        for (int green = 0; green <= 1; green++)
                            for (int blue = 0; blue <= 1; blue++)
                            {
                                colorList.Add(Color.FromArgb(red * bright, green * bright, blue * bright));
                            }
            }
            else if (colorNumber == PaletteColorNumber.Colors125)
            {
                for (int red = 0; red <= 256; red += 64)
                    for (int green = 0; green <= 256; green += 64)
                        for (int blue = 0; blue <= 256; blue += 64)
                        {
                            colorList.Add(Color.FromArgb(Math.Min(red, 255), Math.Min(green, 255), Math.Min(blue, 255)));
                        }
            }

            RecomputeSize();
        }

        public void RecomputeSize()
        {
#if !PocketPC
            {
                // This is a hack to force control to recompute its size.
                // Don't ask me why it works - it was by trial and error
                // that I came to this magic sequence.
                IntPtr tempHandle = this.Handle;
                bool tempSHK = this.ShowFocusCues;
            }
#endif

            if (Dock == DockStyle.Left || Dock == DockStyle.Right)
            {
                int elementsInCol = (this.Height - spanSize) / rectangleSize;
                colorsInRow = (int) Math.Ceiling((colorList.Count + 4) / (double) elementsInCol);
                if (colorsInRow < 2)
                    colorsInRow = 2;

                this.Width = colorsInRow * rectangleSize + spanSize;
            }
            else //if (Dock == DockStyle.Top || Dock == DockStyle.Bottom)
            {
                colorsInRow = (this.Width - spanSize) / rectangleSize;
                int elementsInCol = (int) Math.Ceiling((colorList.Count + 4) / (double) colorsInRow);
                if (elementsInCol < 2)
                    elementsInCol = 2;

                this.Height = elementsInCol * rectangleSize + spanSize;
            }
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            //base.OnPaintBackground(e);
        }

        protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
        {
            rectangleSize = (int) (rectangleSize * factor.Width);
            spanSize = (int) (spanSize * factor.Width);
            base.ScaleControl(factor, specified);
        }

        protected virtual void OnCloseParentDropDown()
        {
            if (CloseParentDropDown != null)
                CloseParentDropDown(this, EventArgs.Empty);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            int rx = (e.X - spanSize / 2) / rectangleSize;
            int ry = (e.Y - spanSize / 2) / rectangleSize;

            if (rx >= 0 || ry >= 0)
            {
                if (rx <= 1 && ry <= 1)
                {
                    ColorDialog cd = new ColorDialog(false);
                    cd.ColorEdit = selectedColor;
                    if (cd.ShowDialog() == DialogResult.OK)
                    {
                        SelectedColor = cd.ColorEdit;
                    }
                    cd.Dispose();

                    OnCloseParentDropDown();
                }
                else
                {
                    int i = -1;
                    if (ry >= 1)
                    {
                        i = ry * colorsInRow - 4 + rx;
                    }
                    else //if (ry == 0)
                    {
                        i = rx - 2;
                    }

                    if (i >= 0 && i < colorList.Count)
                    {
                        SelectedColor = colorList[i];
                    }

                    OnCloseParentDropDown();
                }
            }

            base.OnMouseDown(e);
        }

        void DrawColorRectangle(int x, int y, Graphics gr, Color c)
        {
            gr.DrawRectangle(new Pen(ForeColor),
                (spanSize + x),
                (spanSize + y),
                (rectangleSize - spanSize),
                (rectangleSize - spanSize));
            gr.FillRectangle(new SolidBrush(c),
                (spanSize + x) + 1,
                (spanSize + y) + 1,
                (rectangleSize - spanSize) - 1,
                (rectangleSize - spanSize) - 1);
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            Bitmap bmp = new Bitmap(Width, Height, PixelFormat.Format32bppRgb);
            Graphics gr = Graphics.FromImage(bmp);
            gr.FillRectangle(new SolidBrush(BackColor), 0, 0, Width, Height);
            gr.DrawRectangle(new Pen(ForeColor), 0, 0, Width - 1, Height - 1);
            gr.DrawRectangle(new Pen(ForeColor),
                spanSize,
                spanSize,
                (2 * rectangleSize - spanSize),
                (2 * rectangleSize - spanSize));

            DrawColorRectangle(rectangleSize / 2, rectangleSize / 2, gr, selectedColor);
            int counter = 0;
            if (colorList.Count > 0 && colorsInRow > 0)
            {
                for (int row = 0; ; row++)
                {
                    int colStart = 0;
                    if (row < 2)
                        colStart = 2;

                    for (int col = colStart; col < colorsInRow; col++)
                    {
                        DrawColorRectangle(col * rectangleSize, row * rectangleSize, gr, colorList[counter]);
                        counter++;
                        if (counter >= colorList.Count)
                            break;
                    }

                    if (counter >= colorList.Count)
                        break;
                }
            }

            pe.Graphics.DrawImage(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height), new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
            gr.Dispose();
            bmp.Dispose();
            base.OnPaint(pe);
        }

        public event EventHandler CloseParentDropDown;

        public Size GetOptimalSize(int maxWidth, int maxHeight)
        {
            int elementsInCol = (maxHeight - spanSize) / rectangleSize;
            colorsInRow = (int) Math.Ceiling((colorList.Count + 4) / (double) elementsInCol);
            if (colorsInRow < 2)
                colorsInRow = 2;

            int width = colorsInRow * rectangleSize + spanSize;
            int height = (int) Math.Ceiling((colorList.Count + 4) / (double) colorsInRow) * rectangleSize + spanSize;

            if (width > maxWidth)
            {
                colorsInRow = (maxWidth - spanSize) / rectangleSize;
                if (colorsInRow < 2)
                    colorsInRow = 2;
                elementsInCol = (int) Math.Ceiling((colorList.Count + 4) / (double) colorsInRow);
                if (elementsInCol < 2)
                {
                    elementsInCol = 2;
                    colorsInRow = (int) Math.Ceiling((colorList.Count + 4) / (double) elementsInCol);
                }

                width = colorsInRow * rectangleSize + spanSize;
                height = elementsInCol * rectangleSize + spanSize;
            }

            return new Size(width, height);
        }

        public float Area { get { return (ColorList.Count + 4) * rectangleSize * rectangleSize; } }

        public void CloseDropDown() { }
        public void ReleaseResources() { }
        public void Initialize() { }
        public bool DisposeWithForm { get { return false; } }

        public Control Control { get { return this; } }
    }
}
